home *** CD-ROM | disk | FTP | other *** search
/ PC-Blue - MS DOS Public Domain Library / PC-Blue MS-DOS Public Domain Library - NYACC.iso / vol350 / prolog.arc / CHART.ARC / CHART.TXT < prev    next >
Encoding:
Text File  |  1987-03-31  |  11.8 KB  |  217 lines

  1. .pn1
  2. .po4
  3. A VERY SIMPLE CHART PARSER
  4.  
  5. Peter Ross
  6. Dept. of AI, University of Edinburgh,
  7. 80 South Bridge,
  8. Edinburgh EH1 1HN,
  9. Scotland.
  10.  
  11. This describes how to use the (very) simple chart parser written in Prolog
  12. in the file "chart.pro". You will need LOTS of stack space - the program
  13. does no assertion or retraction at run time, but keeps all the run-time
  14. data in lists.
  15.  
  16. The program expects data in the form of Prolog clauses:
  17.  
  18. [1] rule(Tag, Category, ExpansionList).
  19.         The 'Tag' argument is just an arbitrary Prolog term used to group
  20.         together a set of rule/3 and lexical/3 clauses. The parser needs
  21.         to be told this tag in order to know which set of these clauses
  22.         to use as data. Since it is arbitrary, you could use a compound
  23.         term to allow you to selectively include rules, at no extra cost.
  24.         The 'Category' argument identifies a syntactic category, e.g.
  25.         noun or sentence.
  26.         The 'ExpansionList' is a list giving one possible expansion of that
  27.         category into sub-categories.
  28.         Example:
  29.                 rule(simple, sentence, [noun_phrase, verb_phrase]).
  30.         The categories (both Category and those in ExpansionList) can be
  31.         compound terms, thus allowing the use of variables for result passing
  32.         and context setting. The parser copies terms as needed, when
  33.         unification might be a bad thing to do because it might cause
  34.         supposedly independent data structures to become too specific.
  35.         This copying, by the way, is done by dismantling the source structure
  36.         and building a duplicate rather than by the crude assert/retract
  37.         method; the latter tends to be much slower but takes less run-time
  38.         space.
  39.  
  40.         NOTE: categories are not supposed to be defined recursively.
  41.         Nevertheless, the code does check whether you are adding an edge you
  42.         already have. The check does slow things a bit, but is specifically
  43.         a check against left recursion at the time when empty active edges
  44.         are added. A fuller check against adding duplicate inactive edges
  45.         has been commented out; you probably don't need it unless you mess
  46.         with the rule tags at run time so as to cause new parsing rules to
  47.         be introduced during parsing.
  48.  
  49. [2] lexical(Tag, Word, CategoryList).
  50.         The 'Tag' argument is as above.
  51.         The 'Word' is a word that may legitimately appear in the input.
  52.         The 'CategoryList' is a list of the possible syntactic categories
  53.         which that word might fit, e.g. 'fly' might be a noun or verb.
  54.         As above, the categories can be arbitrary terms.
  55.  
  56. [3] initial_category(Tag, Category).
  57.         This states what the highest level category is - it should be unique.
  58. è
  59. [4] strategy(Tag, Strategy).
  60.         This should instantiate Strategy to either 'td' for top-down or
  61.         'bu' for bottom-up. The parser does not check it for validity.
  62.  
  63. [5] policy(Tag, Policy).
  64.         This should instantiate Policy to either 'df' for depth-first or
  65.         'bf' for breadth-first. The parser does not check it for validity.
  66.  
  67. [6] ersatz_category(Tag, DummyCategoryName).
  68.         By default, the category name 'user' is specially used by the system.
  69.         It supposes that there is an extra rule of the form
  70.                 rule(Tag, user, [TopLevelCategory])
  71.         when doing top-down parsing. This extra category will of course appear
  72.         in the final chart, and should help in the later analysis of it. If
  73.         you are really attached to the category name 'user' but want to do
  74.         some top-down parsing, then you can persuade the system to use a
  75.         different name instead by including a clause of this form.
  76.         Although this ersatz category can be useful, its original purpose
  77.         was to make the program shorter at negligible speed cost by ensuring
  78.         that the top-level category appeared on the left of a single rule.
  79.  
  80. IMPORTANT: The rule/3 and lexical/3 predicates are only used during the
  81.            initialisation stage, when the transformed rules are constructed
  82.            and when the initial chart is created.
  83.  
  84. There are three important data structures manipulated by the program:
  85.  
  86. [a] edge(Category, FoundList, NeedsList, StartVertex, EndVertex).
  87.         This describes an edge in the chart. If you don't know what this
  88.         means, read Winograd's book "Language As A Cognitive Process, vol 1"
  89.         or the articles by Henry Thompson and Graeme Ritchie in "Artificial
  90.         Intelligence: Tools, Techniques and Applications" by O'Shea and
  91.         Eisenstadt (published by Harper and Row).
  92.         There are three odd details. Items on the 'FoundList' are of the form
  93.                 Category = VertexNumber
  94.         (the starting, left-hand vertex of that instance of the category).
  95.         A word from the input will appear in the 'FoundList' as
  96.                 word(Word) = VertexNumber
  97.         The 'FoundList' is ordered so that the most recently found category is
  98.         first.
  99.  
  100. [b] the chart: ActiveEdgeList + InactiveEdgeList.
  101.         The edges are segregated into two lists for convenience.
  102.         The parser returns such a chart when it finishes.
  103.  
  104. [c] the agenda: CandidateList - Hole.
  105.         This is a difference list (a list with a variable at the end - 'Hole'
  106.         is the variable). The items in the list are of the form
  107.                 ActiveEdge + InactiveEdge
  108.         and are candidates (guaranteed successful, in fact) for an application
  109.         of the fundamental rule of chart parsing.
  110.         The default monitoring system prints out agendas.
  111.  
  112. The program has two top level predicates. They are:
  113. è
  114. [i]   parse(Tag, WordList, MaxVertex, Chart).
  115.         The Tag and WordList must be given. The MaxVertex and Chart must be
  116.         variables. When the parse is done, MaxVertex will be instantiated to
  117.         the largest vertex number (the lowest is 0), and Chart will be
  118.         instantiated to the final chart. When doing top-down parsing, the
  119.         parser adds one ersatz rule of the form
  120.                 rule(Tag,user,[TopMostCategory])
  121.         - there will be edges for the ersatz category 'user' to help you to
  122.         examine the chart afterwards for successful parsings. You can
  123.         substitute what you want for 'user' - see above. This predicate does
  124.         some transformations on the rule/3 clauses supplied by the user. The
  125.         transformations are all done by the predicate invert_rules(Tag).
  126.  
  127. [ii]  make_chart(Tag, WordList, MaxVertex, Chart).
  128.         This is exactly like parse/4 above, but assumes that the rule
  129.         transformations have already been done.
  130.  
  131. When a parse has been done and a final chart has been produced, you may
  132. want to add to the word list and extend the chart, for example if using the
  133. parser for plan recognition. You can do so by the following means: call
  134.         initial_setup(Tag, Strategy, Policy, NewWordList,
  135.                       MinVertex, NewMaxVertex,
  136.                       OldChart, NewInitialChart,
  137.                       AgendaOfYourChoice, NewAgenda),
  138.         chart(Tag, Strategy, Policy, NewInitialChart, NewAgenda, FinalChart)
  139. This will assume that more words are added between MinVertex and NewMaxVertex,
  140. and will add to the OldChart and AgendaOfYourChoice you gave it, to create
  141. a new FinalChart. Since all the information needed about the presence of
  142. any previous set of words (presumably ending at MinVertex) will be contained
  143. in the OldChart you got out of parsing that previous word sequence, this will
  144. incrementally add to the chart.
  145.  
  146. Normally parsing stops when the parser runs out of agenda. You may discover
  147. a need to suspend parsing under program control, for example if you want
  148. to doctor the chart or extract useful information from it before the entire
  149. input sequence is available. If so, you may redefine the test
  150.         stop_parser(Tag,Strategy,Policy,Chart,Agenda,FinalChart)
  151. which currently does no more than instantiate the result FinalChart to
  152. Chart when Agenda is empty. It is the success of this predicate which halts
  153. parsing.
  154.  
  155. The implementation of the fundamental rule is explicit within the code, so
  156. that it is easy to change for your own purposes.
  157.  
  158.  
  159. There is a simple monitoring mechanism. The predicates
  160.         watch(Tag)      nowatch(Tag)
  161. turn it on or off for the specified rule set. If on, it reports when the
  162. rule transformations are complete, and whenever an item on the agenda is
  163. processed, it tries to call the predicate
  164.         user_mon(Tag,Strategy,Policy,OldChart,OldAgenda,NewChart,NewAgenda)
  165. If this fails, it uses a default strategy of printing out the new chart and
  166. agenda and waiting for a <cr> before continuing.
  167.  
  168. èThere is a simple test rig, called by the predicate
  169.         test(Tag).
  170. It makes use of a trivial utility
  171.         print_chart(Chart)
  172. to print out the active and inactive edges after the parse has finished.
  173. The lists of edges are sorted before being printed, into the standard order
  174. of Prolog terms.
  175.  
  176. A sample rule set can be found in the files "test01.pro" and "test02.pro".
  177.  
  178.  
  179. IMPLEMENTATION: SOME THOUGHTS
  180.  
  181. This program clearly shows the need for some kind of user-definable indexing
  182. in Prolog in order to make such things efficient. Unfortunately record/3 is
  183. not much use, because the key can only usefully be atomic (only the principal
  184. functor name matters) and in C-Prolog the key cannot be an integer (nor is
  185. record/3 that fast in C-Prolog). At present all you've really got is the
  186. clause indexing mechanism, so you're stuck with assert/retract and all that
  187. that implies if you want to cut down on searching through big data structures
  188. such as long lists. Gimme arrays, gimme hunks, gimme a decent record package,
  189. gimme hash tables, gimme something!! Prolog-2 does let you specify hash tables
  190. for particular predicates, but these are only used by the calling mechanism.
  191. Wouldn't it be nice to have some way of specifying that anything with a given
  192. principal functor was to be accessed through a set of hash tables,
  193. sub-indexing specifiable by the user in some way, e.g. akin to that offered
  194. by PEARL for frame access? Some other Prologs (e.g. Arity Prolog) do now
  195. offer such things as B-trees and hash tables for term storage.
  196.  
  197. At the moment, for simplicity, the chart is only represented as two lists -
  198. one of active edges, one of inactive edges. Since the chart only grows, never
  199. shrinks, it might be better to represent it as two ordered trees peppered with
  200. holes (variables). This ought to speed things up a lot.
  201.  
  202. I considered the idea of not allowing Prolog variables in the terms which
  203. represent categories; this would end all temptation to misuse unification.
  204. Instead, I have in the past used terms of the form ?Atom as variables (where
  205. ?/1 is defined as a prefix operator, none of this messy atom exploding done
  206. by LISPers who don't/can't alter their character tables). This leads to
  207. passing binding lists about a lot. As far as the ersatz unification in edge
  208. generation goes, there is no significant speed difference between using
  209. Prolog variables and these pseudo-variables. However, there is a difference
  210. in speed when looking for candidate edge pairs, since with Prolog variables
  211. I can just use the dodge of specifying \+(\+(Category1=Category2)), which
  212. checks whether they will unify, without doing so, in a reasonably efficient
  213. way. With pseudo-variables I'd need to indulge in term smashing or suffer
  214. the overhead of carrying binding lists about in the chart. The latter is not
  215. so awful as it might look, since it would be possible to construct the unifier
  216. at the same time as checking candidacy, and just carry the unifier about till
  217. needed. But, this still looks a bit worse than using Prolog variables.
  218.  
  219.  
  220.